// PR c++/67148
// { dg-do compile { target c++20 } }
// { dg-additional-options "-fconcepts" }

namespace std
{
  template<typename T>
  T declval();

  typedef unsigned int size_t;
  typedef int ptrdiff_t;
  typedef decltype(nullptr) nullptr_t;
  template<typename _Tp, _Tp... _Idx>
    struct integer_sequence
    {
      typedef _Tp value_type;
      static constexpr size_t size() { return sizeof...(_Idx); }
    };

  template <class T, T Value>
  struct integral_constant {
    using type = integral_constant;
    using value_type = T;
    constexpr operator T() const { return Value; }
    constexpr T operator()() const { return Value; }
    static constexpr T value {Value};
  };
  template <class T, T Value>
  constexpr T integral_constant<T, Value>::value;
  using true_type = integral_constant<bool, true>;
  using false_type = integral_constant<bool, false>;

  template <class T, class U>
  struct is_same : false_type {};
  template <class T>
  struct is_same<T,T> : true_type {};
}
       
namespace meta
{
    inline namespace v1
    {
        template <typename T>
        using _t = typename T::type;
        template <bool... Bools>
        using and_c = std::is_same<std::integer_sequence<bool, Bools...>,
                                   std::integer_sequence<bool, (Bools || true)...>>;
    }
}

namespace stl2 { inline namespace v1 {
using std::declval;
namespace detail {
template <class...>
struct all_same : std::true_type {};
template <class T, class...Rest>
struct all_same<T, Rest...> :
  meta::and_c<__is_same_as(T, Rest)...> {};
}
template <class...Ts>
concept Same =
  detail::all_same<Ts...>::value;

template <class F, class...Args>
using ResultType = decltype(declval<F>()(declval<Args>()...));
template <class>
struct value_type {};
template <class T>
struct value_type<T*> {
  using type = T;
};
template <class T>
using ValueType =
  typename value_type<T>::type;

template <class F, class...Args>
concept Function =
  requires (F& f, Args&&...args) {
    f((Args&&)args...);
    requires Same<decltype(f((Args&&)args...)), ResultType<F, Args...> >;
  };


template <class, class...> struct __function : std::false_type {};
template<typename F, typename... Args>
  requires Function<F, Args...>
struct __function<F, Args...> : std::true_type {};

template <class F, class I1, class I2>
concept IndirectCallable =
  Function<F, ValueType<I1>, ValueType<I2>>;


template <class F, class I1, class I2>
concept IndirectCallable2 =
  __function<F, ValueType<I1>, ValueType<I2>>::value;


namespace ext { namespace models {
template <class, class, class>
constexpr bool indirect_callable() { return false; }
template<typename F, typename I1, typename I2>
  requires IndirectCallable<F, I1, I2>
constexpr bool indirect_callable() { return true; }

template <class, class, class>
constexpr bool indirect_callable2() { return false; }
template<typename F, typename I1, typename I2>
  requires IndirectCallable<F, I1, I2>
constexpr bool indirect_callable2() { return true; }
}}
}}

namespace models = stl2::ext::models;

template <class T = void>
struct plus {
  T operator()(T, T) const;
};

static_assert((models::indirect_callable<::plus<int>, int*, int*>()));
static_assert((models::indirect_callable2<::plus<int>, int*, int*>()));

static_assert((models::indirect_callable<::plus<int>, int**, int*>())); // { dg-error "static assertion failed" }
static_assert((models::indirect_callable2<::plus<int>, int**, int*>())); // { dg-error "static assertion failed" }
